#include <iostream>
#include <string>
#include <cstdio>
#include <cstring>
#include <cerrno>
#include <unistd.h>
#include <pthread.h>
#include <functional>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/wait.h>

#include "Common.hpp"
#include "Log.hpp"
#include "InetAddr.hpp"
#include "ThreadPool.hpp"

using namespace My_Log;
using namespace My_ThreadPool;

const in_port_t defport = 8080;
const std::string defip = "127.0.0.1";

using handtask_t = std::function<void()>;
using task_t = std::function<std::string(std::string)>;

class TcpServer
{
private:
    class ThreadData
    {
    public:
        int sockfd;
        TcpServer *self;
    };

    void HanderRequest(int sockfd)
    {
        while (true)
        {
            char recvbuf[1024] = {0};
            int n = recv(sockfd, recvbuf, sizeof(recvbuf) - 1, 0);
            if (n == 0)
            {
                LOG(LogLevel::INFO) << "client close";
                break;
            }
            else if (n < 0)
            {
                LOG(LogLevel::WARNING) << "recv: " << strerror(errno);
                break;
            }
            recvbuf[n] = '\0';
            LOG(LogLevel::INFO) << "recvbuf is : " << recvbuf;

            // ::send(sockfd, recvbuf, n, 0);
            std::string res = _task(std::string(recvbuf));
            ::send(sockfd, res.c_str(), res.size(), 0);
        }
        ::close(sockfd);
    }
    static void *ThreadRequest(void *args)
    {
        pthread_detach(pthread_self());
        ThreadData *data = (ThreadData *)args;
        data->self->HanderRequest(data->sockfd);
        return nullptr;
    }

public:
    TcpServer(task_t task, in_port_t port = defport, std::string ip = defip)
        : _task(task), _listensockfd(-1), _isrunning(false), _addr(port)
    {
    }
    void InitServer()
    {
        // 创建套接字
        _listensockfd = socket(AF_INET, SOCK_STREAM, 0);
        if (_listensockfd < 0)
        {
            LOG(LogLevel::FATAL) << "socket err: " << strerror(errno);
            exit(SOCKET_ERR);
        }
        LOG(LogLevel::INFO) << "socket success";

        // bind套接字
        int n = bind(_listensockfd, _addr.NetAddr(), _addr.NetAddrLen());
        if (n < 0)
        {
            LOG(LogLevel::FATAL) << "bind: " << strerror(errno);
            exit(BIND_ERR);
        }
        LOG(LogLevel::INFO) << "bind success";

        // 监听套接字
        n = listen(_listensockfd, 4);
        if (n < 0)
        {
            LOG(LogLevel::FATAL) << "listen: " << strerror(errno);
            exit(LISTEN_ERR);
        }
        LOG(LogLevel::INFO) << "listen success";
    }
    void Start()
    {
        _isrunning = true;

        while (true)
        {
            struct sockaddr_in cliaddr;
            socklen_t len = sizeof(cliaddr);

            int sockfd = accept(_listensockfd, CONV(&cliaddr), &len);
            if (sockfd < 0)
            {
                LOG(LogLevel::WARNING) << "accept: fail";
            }

            LOG(LogLevel::INFO) << "accept success, sockfd is : " << sockfd;

            // 打印客户信息
            InetAddr cli(cliaddr);
            LOG(LogLevel::INFO) << "client info is : " << cli.GetStrAddr();

            // // 单进程版本
            // HanderRequest(sockfd);

            // 多进程版本
            pid_t id = fork();
            if (id == 0)
            {
                // 子进程
                ::close(_listensockfd); // 关闭不需要的文件描述符
                if (fork() > 0)
                    exit(0);
                HanderRequest(sockfd);
                exit(0);
            }
            ::close(sockfd);
            int rid = ::waitpid(id, nullptr, 0);
            if(rid < 0)
            {
                LOG(LogLevel::WARNING) << "waitpid err";
            }

            // 多线程版本
            ThreadData *td = new ThreadData;
            td->self = this;
            td->sockfd = sockfd;
            pthread_t pid;
            pthread_create(&pid, nullptr, ThreadRequest, td);

            // 线程池版本
            //  task_t func = std::bind(&TcpServer::HanderRequest, this, sockfd);
            //  ThreadPool<task_t>::getinstance()->Equeue(func);
            ThreadPool<handtask_t>::getinstance()->Equeue([sockfd, this]()
                                                          { this->HanderRequest(sockfd); });
        }
    }
    ~TcpServer()
    {
    }

private:
    int _listensockfd;
    InetAddr _addr;
    bool _isrunning;

    task_t _task;
};